/**
 * \file
 */
#include "precompiled.hpp"
#include "kfs.hpp"
#include "kfs_impl.hpp"
#include "Exception.hpp"
#include <cstring>
#include <vector>

using std::vector;


uint8_t*
kfs::Impl::createBlockBuffer()
{
    uint8_t* buffer = new uint8_t[mb.blockNumBytes];
    memset(buffer, 0, mb.blockNumBytes);
    return buffer;
}


//// Explicit template instantiation
//template
//uint8_t const*
//deserializeFixed<uint64_t>(std::vector<uint64_t>& dest,
//    std::size_t numElements,
//    uint8_t const* sourceBuffer);

//--------------------------------------------------

uint8_t const*
kfs::Impl::deserialize(MasterBlock& mb, uint8_t const* sourceBuffer)
{
    uint8_t const* ptr = sourceBuffer;
    uint32_t magic;
    ptr = deserializeFixed(magic, ptr);
    if(magic != MasterBlock::Magic)
    {
        throw Exception("Error: this device does not contain a kfs file "
            "system. Magic number is not found.");
    }
    ptr = deserializeFixed(mb.blockNumBytes, ptr);
    ptr = deserializeFixed(mb.numBlocks, ptr);
    return ptr;
}


uint8_t*
kfs::Impl::serialize(MasterBlock const& mb, uint8_t* destBuffer)
{
    uint8_t* ptr = destBuffer;
    ptr = serializeFixed(MasterBlock::Magic, ptr);
    ptr = serializeFixed(mb.blockNumBytes, ptr);
    ptr = serializeFixed(mb.numBlocks, ptr);
    std::size_t slack = this->mb.blockNumBytes - (ptr - destBuffer);
    memset(ptr, '-', slack);
    ptr += slack;
    return ptr;
}


uint8_t const*
kfs::Impl::deserialize(Inode& inode, uint8_t const* sourceBuffer)
{
    uint8_t const* ptr = sourceBuffer;
    uint32_t magic;
    ptr = deserializeFixed(magic, ptr);
    if(magic != Inode::Magic)
    {
        printf("Error: Invalid inode; magic number is not found expected %x found %x.\n",
            Inode::Magic, magic);
        throw Exception("Error: Invalid inode; magic number is not found.");
    }
    ptr = deserializeFixed(inode.sizeNumBytes, ptr);
    ptr = deserializeFixed(inode.userID, ptr);
    ptr = deserializeFixed(inode.groupID, ptr);
    ptr = deserializeFixed(inode.mode, ptr);
    ptr = deserializeFixed(inode.ctime, ptr);
    ptr = deserializeFixed(inode.mtime, ptr);
    ptr = deserializeFixed(inode.atime, ptr);
    ptr = deserializeFixed(inode.linkCount, ptr);
    ptr = deserializeFixed(inode.dataBlockAddresses, 15, ptr);
    return ptr;
}


uint8_t*
kfs::Impl::serialize(Inode const& inode, uint8_t* destBuffer)
{
    uint8_t* ptr = destBuffer;
    ptr = serializeFixed(Inode::Magic, ptr);
    ptr = serializeFixed(inode.sizeNumBytes, ptr);
    ptr = serializeFixed(inode.userID, ptr);
    ptr = serializeFixed(inode.groupID, ptr);
    ptr = serializeFixed(inode.mode, ptr);
    ptr = serializeFixed(inode.ctime, ptr);
    ptr = serializeFixed(inode.mtime, ptr);
    ptr = serializeFixed(inode.atime, ptr);
    ptr = serializeFixed(inode.linkCount, ptr);
    ptr = serializeFixed(inode.dataBlockAddresses, 15, ptr);
    std::size_t slack = mb.blockNumBytes - (ptr - destBuffer);
    memset(ptr, 0, slack);
    ptr += slack;
    return ptr;
}


template<typename FixedElt>
uint8_t const*
deserializeFixed(
    vector<FixedElt>& dest,
    std::size_t numElements,
    uint8_t const* sourceBuffer)
{
    uint8_t const* ptr = sourceBuffer;
    dest.resize(numElements);
    for(std::size_t i = 0; i < numElements; ++i)
    {
        memcpy(&dest[i], ptr, sizeof(FixedElt));
        ptr += sizeof(FixedElt);
    }
    return ptr;
}


template<typename FixedElt>
uint8_t*
serializeFixed(
    vector<FixedElt> const& source,
    std::size_t numElements,
    uint8_t* destBuffer)
{
    if(source.size() != numElements)
        throw Exception("serialze: Whoops: vector has not the expected "
                "number of elements");
    uint8_t* ptr = destBuffer;
    for(std::size_t i = 0; i < numElements; ++i)
    {
        memcpy(ptr, &source[i], sizeof(FixedElt));
        ptr += sizeof(FixedElt);
    }
    return ptr;
}


template<typename Fixed>
uint8_t const* deserializeFixed(Fixed& dest, uint8_t const* sourceBuffer)
{
    memcpy(&dest, sourceBuffer, sizeof(dest));
    return sourceBuffer + sizeof(dest);
}


template<typename Fixed>
uint8_t* serializeFixed(Fixed const& source, uint8_t* destBuffer)
{
    memcpy(destBuffer, &source, sizeof(source));
    return destBuffer + sizeof(source);
}
